home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Extensions / Imaging / PIL / GifImagePlugin.py < prev    next >
Encoding:
Text File  |  2000-06-23  |  7.5 KB  |  357 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: GifImagePlugin.py,v 1.1.1.1 1998/08/18 13:07:51 sjoerd Exp $
  4. #
  5. # GIF file handling
  6. #
  7. # History:
  8. # 95-09-01 fl    Created
  9. # 96-12-14 fl    Added interlace support
  10. # 96-12-30 fl    Added animation support
  11. # 97-01-05 fl    Added write support, fixed local colour map bug
  12. # 97-02-23 fl    Make sure to load raster data in getdata()
  13. # 97-07-05 fl    Support external decoder
  14. # 98-07-09 fl    Handle all modes when saving
  15. # 98-07-15 fl    Renamed offset attribute to avoid name clash
  16. #
  17. # Copyright (c) Secret Labs AB 1997-98.
  18. # Copyright (c) Fredrik Lundh 1995-97.
  19. #
  20. # See the README file for information on usage and redistribution.
  21. #
  22.  
  23.  
  24. __version__ = "0.5"
  25.  
  26.  
  27. import array
  28. import Image, ImageFile, ImagePalette
  29.  
  30.  
  31. # --------------------------------------------------------------------
  32. # Helpers
  33.  
  34. def i16(c):
  35.     return ord(c[0]) + (ord(c[1])<<8)
  36.  
  37. def o16(i):
  38.     return chr(i&255) + chr(i>>8&255)
  39.  
  40.  
  41. # --------------------------------------------------------------------
  42. # Identify/read GIF files
  43.  
  44. def _accept(prefix):
  45.     return prefix[:6] in ["GIF87a", "GIF89a"]
  46.  
  47. class GifImageFile(ImageFile.ImageFile):
  48.  
  49.     format = "GIF"
  50.     format_description = "Compuserve GIF"
  51.  
  52.     def data(self):
  53.     s = self.fp.read(1)
  54.     if s and ord(s):
  55.         return self.fp.read(ord(s))
  56.     return None
  57.  
  58.     def _open(self):
  59.  
  60.     # Screen
  61.     s = self.fp.read(13)
  62.     if s[:6] not in ["GIF87a", "GIF89a"]:
  63.         raise SyntaxError, "not a GIF file"
  64.  
  65.     self.info["version"] = s[:6]
  66.  
  67.     self.size = i16(s[6:]), i16(s[8:])
  68.  
  69.     self.tile = []
  70.  
  71.     flags = ord(s[10])
  72.  
  73.     bits = (flags & 7) + 1
  74.  
  75.     if flags & 128:
  76.         # get global palette
  77.         self.info["background"] = ord(s[11])
  78.         self.global_palette = self.palette =\
  79.         ImagePalette.raw("RGB", self.fp.read(3<<bits))
  80.  
  81.     self.__fp = self.fp # FIXME: hack
  82.     self.__offset = 0
  83.     self.dispose = None
  84.  
  85.     self.frame = -1
  86.     self.seek(0) # get ready to read first frame
  87.  
  88.     def seek(self, frame):
  89.  
  90.     # FIXME: can only seek to next frame; should add rewind capability
  91.     if frame != self.frame + 1:
  92.         raise ValueError, "cannot seek to frame %d" % frame
  93.     self.frame = frame
  94.  
  95.     self.tile = []
  96.  
  97.     self.fp = self.__fp
  98.     if self.__offset:
  99.         # backup to last frame
  100.         self.fp.seek(self.__offset)
  101.         while self.data():
  102.         pass
  103.         self.__offset = 0
  104.  
  105.     if self.dispose:
  106.         self.im = self.dispose
  107.         self.dispose = None
  108.  
  109.     self.palette = self.global_palette
  110.  
  111.     while 1:
  112.  
  113.         s = self.fp.read(1)
  114.         if not s or s == ";":
  115.         break
  116.  
  117.         elif s == "!":
  118.         #
  119.         # extensions
  120.         # 
  121.         s = self.fp.read(1)
  122.         block = self.data()
  123.         if ord(s) == 249:
  124.             #
  125.             # graphic control extension
  126.             #
  127.             flags = ord(block[0])
  128.             if flags & 1:
  129.             self.info["transparency"] = ord(block[3])
  130.             self.info["duration"] = i16(block[1:3]) * 10
  131.             try:
  132.             # disposal methods
  133.             if flags & 8:
  134.                 # replace with background colour
  135.                 self.dispose = Image.core.fill("P", self.size,
  136.                 self.info["background"])
  137.             elif flags & 16:
  138.                 # replace with previous contents
  139.                 self.dispose = self.im.copy()
  140.             except (AttributeError, KeyError):
  141.             pass
  142.         elif ord(s) == 255:
  143.             #
  144.             # application extension
  145.             #
  146.             self.info["extension"] = block, self.fp.tell()
  147.             if block[:11] == "NETSCAPE2.0":
  148.             self.info["loop"] = 1 # FIXME
  149.         while self.data():
  150.             pass
  151.  
  152.         elif s == ",":
  153.         #
  154.         # local image
  155.         #
  156.         s = self.fp.read(9)
  157.  
  158.         # extent
  159.         x0, y0 = i16(s[0:]), i16(s[2:])
  160.         x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
  161.         flags = ord(s[8])
  162.  
  163.         interlace = (flags & 64) != 0
  164.  
  165.         if flags & 128:
  166.             bits = (flags & 7) + 1
  167.             self.palette =\
  168.             ImagePalette.raw("RGB", self.fp.read(3<<bits))
  169.  
  170.         # image data
  171.         bits = ord(self.fp.read(1))
  172.         self.__offset = self.fp.tell()
  173.         self.tile = [("gif",
  174.                  (x0, y0, x1, y1),
  175.                  self.__offset,
  176.                  (bits, interlace))]
  177.         break
  178.  
  179.         else:
  180.         pass
  181.         # raise IOError, "illegal GIF tag `%x`" % ord(s)
  182.  
  183.     if not self.tile:
  184.         self.__fp = None
  185.         raise EOFError, "no more images in GIF file"
  186.  
  187.     self.mode = "L"
  188.     if self.palette:
  189.         self.mode = "P"
  190.  
  191.     def tell(self):
  192.     return self.frame
  193.  
  194.  
  195. # --------------------------------------------------------------------
  196. # Write GIF files
  197.  
  198. try:
  199.     import _imaging_gif
  200. except ImportError:
  201.     _imaging_gif = None
  202.  
  203. RAWMODE = {
  204.     "1": "L",
  205.     "L": "L",
  206.     "P": "P",
  207. }
  208.  
  209. def _save(im, fp, filename):
  210.  
  211.     if _imaging_gif:
  212.         # call external driver
  213.         try:
  214.             _imaging_gif.save(im, fp, filename)
  215.             return
  216.         except IOError:
  217.             pass # write uncompressed file
  218.  
  219.     try:
  220.     rawmode = RAWMODE[im.mode]
  221.     imOut = im
  222.     except KeyError:
  223.     # convert on the fly (EXPERIMENTAL -- I'm not sure PIL
  224.     # should automatically convert images on save...)
  225.     if Image.getmodebase(im.mode) == "RGB":
  226.         imOut = im.convert("P")
  227.         rawmode = "P"
  228.     else:
  229.         imOut = im.convert("L")
  230.         rawmode = "L"
  231.  
  232.     # header
  233.     for s in getheader(imOut):
  234.     fp.write(s)
  235.  
  236.     flags = 0
  237.  
  238.     try:
  239.     interlace = im.encoderinfo["interlace"]
  240.     except:
  241.     # default is on (since we're writing uncompressed images)
  242.     interlace = 1
  243.     flags = flags | 64
  244.  
  245.     # local image header
  246.     fp.write("," +
  247.          o16(0) + o16(0) +        # bounding box
  248.          o16(im.size[0]) +        # size
  249.          o16(im.size[1]) +
  250.          chr(flags) +        # flags
  251.          chr(8))            # bits
  252.  
  253.     imOut.encoderconfig = (8, interlace)
  254.  
  255.     ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
  256.  
  257.     fp.write("\0") # end of image data
  258.  
  259.     fp.write(";") # end of file
  260.  
  261.     try:
  262.         fp.flush()
  263.     except: pass
  264.  
  265. def _save_netpbm(im, fp, filename):
  266.  
  267.     #
  268.     # If you need real GIF compression and/or RGB quantization, you
  269.     # can use the external NETPBM/PBMPLUS utilities.  See comments
  270.     # below for information on how to enable this.
  271.  
  272.     import os
  273.     file = im._dump()
  274.     if im.mode != "RGB":
  275.     os.system("ppmtogif %s >%s" % (file, filename))
  276.     else:
  277.     os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename))
  278.     try: os.unlink(file)
  279.     except: pass
  280.  
  281.  
  282. # --------------------------------------------------------------------
  283. # GIF utilities
  284.  
  285. def getheader(im):
  286.     """Return a list of strings representing a GIF header"""
  287.  
  288.     s = [
  289.     "GIF87a" +        # magic
  290.     o16(im.size[0]) +    # size
  291.     o16(im.size[1]) +
  292.     chr(7 + 128) +        # flags: bits + palette
  293.     chr(0) +        # background
  294.     chr(0)            # reserved/aspect
  295.     ]
  296.  
  297.     # global palette
  298.     if im.mode == "P":
  299.     # colour palette
  300.     s.append(im.im.getpalette("RGB"))
  301.     else:
  302.     # greyscale
  303.     for i in range(256):
  304.         s.append(chr(i) * 3)
  305.  
  306.     return s
  307.  
  308. def getdata(im, offset = (0, 0), **params):
  309.     """Return a list of strings representing this image.
  310.        The first string is a local image header, the rest contains
  311.        encoded image data."""
  312.  
  313.     class collector:
  314.         data = []
  315.         def write(self, data):
  316.         self.data.append(data)
  317.  
  318.     im.load() # make sure raster data is available
  319.  
  320.     fp = collector()
  321.  
  322.     try:
  323.     im.encoderinfo = params
  324.  
  325.     # local image header
  326.     fp.write("," +
  327.          o16(offset[0]) +    # offset
  328.          o16(offset[1]) +
  329.          o16(im.size[0]) +    # size
  330.          o16(im.size[1]) +
  331.          chr(0) +        # flags
  332.          chr(8))        # bits
  333.  
  334.     ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
  335.  
  336.     fp.write("\0") # end of image data
  337.  
  338.     finally:
  339.     del im.encoderinfo
  340.  
  341.     return fp.data
  342.  
  343.  
  344. # --------------------------------------------------------------------
  345. # Registry
  346.  
  347. Image.register_open(GifImageFile.format, GifImageFile, _accept)
  348. Image.register_save(GifImageFile.format, _save)
  349. Image.register_extension(GifImageFile.format, ".gif")
  350. Image.register_mime(GifImageFile.format, "image/gif")
  351.  
  352. #
  353. # Uncomment the following line if you wish to use NETPBM/PBMPLUS
  354. # instead of the built-in "uncompressed" GIF encoder
  355.  
  356. # Image.register_save(GifImageFile.format, _save_netpbm)
  357.